#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2023/5/31 13:04
# @Author  : huidong.bai
# @File    : TestBaseSuite.py
# @Software: PyCharm
# @Mail    : MasterBai2018@outlook.com
import os
import pdb
import json
import re
import time
import pytest
import allure
import shutil
from queue import Queue
import concurrent.futures
from threading import Event
from conftest import logging
from src.core.Case import TestCase
from src.utils.jsonUtil import JsonUtil
from src.core.Assertion import AssertionFactory
from src.core.Common.Bean.Conveyor import Conveyor
from src.core.Service.TSASession import TSASession
from src.core.ParamEventParse import ParamEventParse
from src.utils.common import uuid, load_yaml_config, get_timestamp
from src.core.Service.ClientSession import ClientSession
from src.core.Common.Bean.XMLReporter import XMLReporter
from src.core.Common.Bean.RuntimeRecorder import RuntimeRecorder
from src.core.NluEventParser import NluEventParser
from src.core.Status.AIBSSessionStatus import AIBSVRConfigCode, AIBSParam, MongoCode
from src.utils.common import read_config, async_color_print, is_available_file
from src.utils.MangoDB import MangoDatabase


class TestBaseSuite:
    dataBus = Queue()                       # 接受回调数据总线队列
    nluEvent = Event()                      # 理解结果收集触发器
    xmlReporter = XMLReporter()             # xml报告的json报告者
    runTimeRecoder = RuntimeRecorder()      # 运行时结果收集器
    nluEventParser: NluEventParser = None   # 事件解析脚本
    conveyor: Conveyor = None               # 外部参数传递者
    yaml_config = None                      # yaml配置对象
    tsa_session: TSASession = None          # tsa助手客户端
    set_session: ClientSession = None       # 语音设置客户端
    sre_session: ClientSession = None       # 声纹客户端
    voi_session: ClientSession = None       # 语音输入法客户端
    oms_session: ClientSession = None       # OMS客户端
    suiteDir = ""                           # 运行Suite工作根路径
    resultPath = ""                         # 结果文件路径
    assert_finish_cond = Event()            # 断言结束的Event事件
    testcase = TestCase()                   # 测试用例
    asserter = AssertionFactory()           # 断言类
    callback_event_type = None              # 理解结果的callback存储
    callback_event_id = None                # 理解结果的callback_etid存储
    thread_pool_executor = None             # 回调数据处理线程池
    report_pool = []                        # 报告数据打印
    calc_recrate = []                       # 计算错误率列表
    callback_result = []                    # 回调函数统计结果集合
    voice_time_list = []                    # 音频时长统计列表
    voiceRegionEndList = []                 # 音频的时间边界
    database = MangoDatabase()              # tts数据库

    @classmethod
    def setup_class(cls, suite_name, tsa_callback=None, sre_callback=None, voi_callback=None):
        from conftest import cmder
        cls.conveyor = cmder
        cls.suiteDir = cls.conveyor.suite_dir
        cls.yaml_config = load_yaml_config(cls.conveyor.parse_yaml)
        cls.resultPath = os.path.join(cls.suiteDir, "result.txt")
        cls.xmlReporter.set_param(cls.conveyor)
        cls.nluEventParser = NluEventParser(cls.conveyor.nlu_callback_path, cls.conveyor.nlu_input_path)

        cls.tsa_session = TSASession(cls.conveyor.aibs_client_path)
        cls.set_session = ClientSession(cls.conveyor.aibs_client_path)
        cls.sre_session = ClientSession(cls.conveyor.aibs_client_path)
        cls.voi_session = ClientSession(cls.conveyor.aibs_client_path)
        cls.oms_session = ClientSession(cls.conveyor.aibs_client_path)

        if tsa_callback:
            cls.tsa_session.set_listener(tsa_callback)
        if sre_callback:
            cls.sre_session.set_listener(sre_callback)
        if voi_callback:
            cls.voi_session.set_listener(voi_callback)

        ret = cls.set_session.create(cls.conveyor.aibs_client_config, cls.conveyor.lang_runner, "com.vr.setting.cn")
        if ret != 0:
            pytest.skip(f"Skip test suite, create com.vr.setting.cn client error: {ret}")
        cls.sre_session.create(cls.conveyor.aibs_client_config, cls.conveyor.lang_runner, "com.toyota.personal.center.cn")
        if ret != 0:
            pytest.skip(f"Skip test suite, create com.toyota.personal.center.cn client error: {ret}")
        cls.voi_session.create(cls.conveyor.aibs_client_config, cls.conveyor.lang_runner, "com.toyota.agent.service.cn")
        if ret != 0:
            pytest.skip(f"Skip test suite, create com.toyota.agent.service.cn client error: {ret}")
        cls.oms_session.create(cls.conveyor.aibs_client_config, cls.conveyor.lang_runner, "com.autoai.oms.cn")
        if ret != 0:
            pytest.skip(f"Skip test suite, create com.autoai.oms.cn client error: {ret}")

        # 设置车参数，默认0，雷克萨斯
        # car_brand = cls.conveyor.case_header.carType.value if cls.conveyor.case_header.carType.value else "0"
        # device_name = cls.conveyor.case_header.device_name.value if cls.conveyor.case_header.device_name.value else None
        # code = cls.tsa_session.set_car_type(car_brand, device_name)
        # if code != 0:
        #     pytest.skip(f"Skip test suite, set tsa car type error: {code}")

        cls.xmlReporter.push_start(suite_name)
        cls.asserter.load_path_yaml(cls.yaml_config)
        cls.asserter.set_reporter(cls.xmlReporter)
        cls.asserter.set_case(cls.testcase)
        cls.asserter.setResultPath(os.path.join(cls.conveyor.suite_dir, "result.txt"))
        cls.asserter.set_finish_event(cls.assert_finish_cond)
        cls.tsa_session.set_finish_event(cls.assert_finish_cond)
        cls.asserter.set_recorder(cls.runTimeRecoder)
        cls.asserter.set_databus(cls.dataBus)
        cls.asserter.set_project(cls.conveyor.project)
        cls.asserter.set_database(cls.database)
        cls.tsa_session.set_recorder(cls.runTimeRecoder)
        cls.thread_pool_executor = concurrent.futures.ThreadPoolExecutor(max_workers=10)

    @classmethod
    def teardown_class(cls):
        cls.tsa_session.free()
        cls.dataBus.queue.clear()

        del cls.tsa_session
        del cls.set_session
        del cls.sre_session
        del cls.voi_session
        del cls.oms_session

        cls.xmlReporter.push_stop()
        del cls

    @pytest.fixture(scope="class", autouse=True, name="MongoTestSuiteFixture")
    def mongo_testsuite_fixture(self):
        async_color_print(("\n" + "=" * 31 + " test suit start " + "=" * 31, None))
        yield
        async_color_print(("=" * 32 + " test suit end " + "=" * 32, None))
    
    @pytest.fixture(scope="function", autouse=True, name="MongoPreTestFixture")
    def mongo_pretest_fixture(self, request):
        async_color_print(("\n" + "=" * 31 + " test case start " + "=" * 31, "green"))
        allure.dynamic.epic(f"[ Solution ] {self.conveyor.solution_space}")
        allure.dynamic.feature(f"[ Scene ] {self.conveyor.workspace}")
        allure.dynamic.story(f"[ Suite ] [{self.conveyor.suite_id}] {self.conveyor.suite_abstract}")
        self.clear()
        yield
        run_cmd = f"python3 Run_Mongo.py -f {self.conveyor.suite_name} -C {self.conveyor.config} -a {self.conveyor.case_list} -p {self.conveyor.source_lcs_config} -F {self.testcase.index}"
        with allure.step("执行命令："): pass
        with allure.step(run_cmd): pass

        # 日志保存开关打开时, 保存日志
        if (not self.asserter.result) and (self.conveyor.log_save_option == "True"):
            self.attach_zip_log()
            # allure attach 音频
            voice_path = self.testcase.input.get("voice")
            self.attach_voice(voice_path)

        async_color_print(("\n" + "="*32 + " test case end " + "=" * 32, "green"))
    
    @pytest.fixture(scope="function", autouse=False, name="testCaseLoader")
    def mongo_testcase_load_fixture(self, request):
        testcase = None
        try:
            testcase = request._pyfuncitem.callspec.params.get("testcase")
            self.testcase.load_case(testcase)
        except Exception as e:
            logging.error(f"加载Case失败: {e}, testcase:{testcase}")
            self.xmlReporter.skip_case_infos.append(f"加载Case失败: {e}, testcase:{testcase}")
            pytest.skip(f"加载Case失败: {e}, testcase:{testcase}")

        with allure.step(f"解析Case: {self.conveyor.case_list}"):
            with allure.step(f"[ Line ] {self.testcase.index}"): pass
            with allure.step(f"[ 输入 ] {self.testcase.input}"): pass
            with allure.step(f"[ 参数 ] {self.testcase.param}"): pass
            with allure.step(f"[ 预期 ] {self.testcase.expect}"): pass
        
        voice = self.testcase.input.get('voice')
        if voice and (not is_available_file(voice)):
            logging.error(f"错误的音频: {voice}")
            self.xmlReporter.skip_case_infos.append(f"错误的音频: {voice}")
            allure.dynamic.title(f"[{self.testcase.index}] {self.testcase.input}")
            pytest.skip(f"错误的音频: {voice}")
        yield
        allure.dynamic.title(f"[{self.testcase.index}] {self.testcase.input}")

    def attach_voice(self, voice_path):
        if voice_path is None:
            return
        if os.path.exists(voice_path):
            if voice_path.endswith('.wav'):
                allure.attach.file(source=voice_path, name=voice_path, attachment_type=allure.attachment_type.WAVE)
            elif voice_path.endswith('.pcm'):
                allure.attach.file(source=voice_path, name=voice_path, attachment_type=allure.attachment_type.PCM)

    def attach_zip_log(self):
        attach_zip_log_path = os.path.join(self.conveyor.solution_space, "allure_attach", uuid())
        log_path = os.path.join(self.suiteDir, "log")
        shutil.make_archive(base_name=attach_zip_log_path, format="zip", base_dir=log_path)
        allure.attach.file(attach_zip_log_path + ".zip", name="运行日志文件", attachment_type=allure.attachment_type.ZIP)

    def clear(self):
        self.runTimeRecoder.reset()
        self.testcase.reset()
        self.asserter.reset()
        self.dataBus.queue.clear()
        self.assert_finish_cond.clear()
        self.nluEvent.clear()
        self.report_pool.clear()

    def set_parameters_pre(self) -> int:
        try:
            expects = json.dumps(self.testcase.expect)
        except Exception as e:
            logging.error(f"Parse testcase expects error: {e}")
            return -1
        cancel = self.testcase.param.get('cancel')
        if cancel:
            self.tsa_session.set_cancel_tag(int(cancel))

        # 默认设置工作模式为2：wakeup_asr
        workMode = self.testcase.param.get('workMode')
        ret = self.tsa_session.set_work_mode(workMode if workMode else "2")
        if ret != 0:
            logging.error(f"Set speech engine work mode error: {ret}")

        # 设置送音频的delay方式
        delay = self.testcase.param.get('delay')
        if delay:
            self.tsa_session.set_delay(float(delay))

        # 设置语种信息 vrLang:cmn
        vrLang = self.testcase.param.get('vrLang')
        if vrLang:
            if isinstance(vrLang, str):
                vrLang = [vrLang]
            _data = {"wakeup": vrLang, "asr": vrLang}
            self.runTimeRecoder.setVRLangCode = self.set_session.set_vr_config(AIBSVRConfigCode.AIBS_SETTING_DIALOGUE_LANGUAGE, _data)
            if self.runTimeRecoder.setVRLangCode != 0:
                logging.error(f"Set speech engine vr lang error: {self.runTimeRecoder.setVRLangCode}")

        # 设置自定义唤醒词 vrWords:你好丰田;vrLang:cmn
        vrWakeupAlias = self.testcase.param.get('vrWakeupAlias')
        if vrWakeupAlias and vrLang:
            if vrWakeupAlias == 'NULL':
                vrWakeupAlias = ''
            _data = {"language": vrLang, "name": vrWakeupAlias, "level": 3}
            self.runTimeRecoder.setVRWakeupAliasCode = self.set_session.set_vr_config(AIBSVRConfigCode.AIBS_SETTING_WAKEUP_ALIAS, _data)

        # 设置唤醒词生效 vrWakeupWord:你好丰田
        vrWakeupWord = self.testcase.param.get('vrWakeupWord')
        if vrWakeupWord and vrLang:
            json_data_vr_word = {"language": vrLang, "name": vrWakeupWord, "enable": 1}
            self.runTimeRecoder.setVRWordOnlineCode = self.set_session.set_vr_config(AIBSVRConfigCode.AIBS_SETTING_WAKEUP_ENABLE, json_data_vr_word)
            if self.runTimeRecoder.setVRWordOnlineCode != 0:
                logging.error(f"Set speech engine vr wakeup word error: {self.runTimeRecoder.setVRWordOnlineCode}")

        # 设置本地实时上屏开关
        offlineButterfly = self.testcase.param.get('offlineButterfly')
        if offlineButterfly:
            ret = self.tsa_session.set_engine_param(AIBSParam.AIBS_PARAM_REAL_TIME_RESULT, int(offlineButterfly))
            if ret != 0:
                logging.error(f"Set speech engine offline butterfly error: {ret}")

        # 设置OneShot的Duration
        delayOneshotDuration = self.testcase.param.get('delayOneshotDuration')
        if delayOneshotDuration:
            ret = self.tsa_session.set_engine_param(AIBSParam.AIBS_PARAM_WAKEUP_DELAY_ONESHOT_DURATION, int(delayOneshotDuration))
            if ret != 0:
                logging.error(f"Set speech engine delay oneshot duration error: {ret}")

        # 设置静音超时时间
        silenceDuration = self.testcase.param.get('silenceDuration')
        if silenceDuration:
            ret = self.tsa_session.set_engine_param(AIBSParam.AIBS_PARAM_SILENCE_DURATION, int(silenceDuration))
            if ret != 0:
                logging.error(f"Set speech engine silence duration error: {ret}")

        # 设置数字转换开关
        digitOption = self.testcase.param.get('digitOption')
        if digitOption:
            ret = self.tsa_session.set_engine_param(AIBSParam.AIBS_PARAM_DIGIT_CONVERT_RESULT, int(digitOption))
            if ret != 0:
                logging.error(f"Set speech engine digit option error: {ret}")

        # 获取版本号
        if "aibsVersion" in expects:
            self.runTimeRecoder.aibsVersion = self.tsa_session.get_version()

        dialog = self.testcase.param.get('dialog')
        if dialog == "start":
            self.tsa_session.set_dialog(1)
            self.callback_event_type = None
            self.callback_event_id = None
        elif dialog == "end":
            self.tsa_session.set_dialog(3)

        return 0

    def set_parameters_after(self):
        try:
            expects = json.dumps(self.testcase.expect)
        except Exception as e:
            logging.error(f"Parse testcase expects error: {e}")
            return -1

        # 获取唤醒词列表
        if "wakeupWordList" in expects or "wakeupWordListAll" in expects or "wakeupWordMain" in expects or "wakeupWordCar" in expects:
            wakeupWords = self.set_session.get_vr_config(AIBSVRConfigCode.AIBS_SETTING_GET_WAKEUP_WORD)
            all_wakeup_words = []
            if wakeupWords:
                for val in json.loads(wakeupWords)["wakeup"]["data"]["fix_data"]:
                    if "," in val["word"]:
                        val["word"] = re.sub(",", "_", val["word"])
                    self.runTimeRecoder.wakeupWordListAll.append(val["word"])
                    if val["enabled"] == True:
                        all_wakeup_words.append(val["word"])
                        self.runTimeRecoder.wakeupWordList = all_wakeup_words
                    if val["isPrimary"] == True:
                        self.runTimeRecoder.wakeupWordMain.append(val["word"])
                    if val["isCarWakeup"] == True:
                        self.runTimeRecoder.wakeupWordCar.append(val["word"])

        # 设置对话模式 vrDialogueMode:1
        vrDialogueMode = self.testcase.param.get('vrDialogueMode')
        if vrDialogueMode:
            self.runTimeRecoder.setVRDialogueModeCode = self.set_session.set_vr_config(AIBSVRConfigCode.AIBS_SETTING_DIALOGUE_STYLE, int(vrDialogueMode))
            if self.runTimeRecoder.setVRDialogueModeCode != 0:
                logging.error(f"Set speech engine vr dialogue mode error: {self.runTimeRecoder.setVRDialogueModeCode}")

        # 设置生效音区,如果Case中未写，则默认设置为全音区生效
        vrSeatSignal = self.testcase.param.get('vrSeatSignal')
        if vrSeatSignal:
            self.runTimeRecoder.setVRSeatSignalCode = self.set_session.set_vr_config(AIBSVRConfigCode.AIBS_SETTING_SOUND_AREA_OPTION, int(vrSeatSignal))
            if self.runTimeRecoder.setVRSeatSignalCode != 0:
                logging.error(f"Set speech engine vr seat signal error: {self.runTimeRecoder.setVRSeatSignalCode}")

        # 设置场景唤醒词开关 vrSceneOption:1
        vrSceneOption = self.testcase.param.get('vrSceneOption')
        if vrSceneOption:
            self.runTimeRecoder.setVRSceneOptionCode = self.set_session.set_vr_config(AIBSVRConfigCode.AIBS_SETTING_WAKEUP_KEYWORD_OPTION, int(vrSceneOption))
            if self.runTimeRecoder.setVRSceneOptionCode != 0:
                logging.error(f"Set speech engine vr wakeup scene option error: {self.runTimeRecoder.setVRSceneOptionCode}")

        # 设置语音助手重置 vrReset:1
        vrReset = self.testcase.param.get('vrReset')
        if vrReset == '1':
            self.runTimeRecoder.setVRResetCode = self.set_session.set_vr_reset()
            if self.runTimeRecoder.setVRResetCode != 0:
                logging.error(f"Set speech engine vr reset error: {self.runTimeRecoder.setVRResetCode}")

        # 设置唤醒词开关 vrOption:1
        vrOption = self.testcase.param.get('vrOption')
        if vrOption:
            self.runTimeRecoder.setVROptionCode = self.set_session.set_vr_config(AIBSVRConfigCode.AIBS_SETTING_VOICE_WAKEUP_OPTION, int(vrOption))
            if self.runTimeRecoder.setVROptionCode != 0:
                logging.error(f"Set speech engine vr wakeup scene option error: {self.runTimeRecoder.setVROptionCode}")
                

        # 设置声纹隐私开关 vrPrintption:1
        vrPrintption = self.testcase.param.get('vrPrintption')
        if vrPrintption:
            self.runTimeRecoder.setVRPrintOptionCode = self.set_session.set_vr_config(AIBSVRConfigCode.AIBS_SETTING_SRE_SENSITIVE_EMPOWER_OPTION, int(vrPrintption))
            if self.runTimeRecoder.setVRPrintOptionCode != 0:
                logging.error(f"Set print option error: {self.runTimeRecoder.setVRPrintOptionCode}")

        # 设置声纹使能开关, 默认为0, sreOption:1
        sreOption = self.testcase.param.get('sreOption')
        if sreOption:
            self.runTimeRecoder.sreOption = self.set_session.set_vr_config(AIBSVRConfigCode.AIBS_SETTING_SRE_FUNC_ENABLE_OPTION, int(sreOption))
            if self.runTimeRecoder.sreOption != 0:
                logging.error(f"Set sre option error: {self.runTimeRecoder.sreOption}")

        # 设置可见即可说页面信息
        hmi = self.testcase.param.get('hmi')
        if hmi:
            ret = self.tsa_session.set_event(hmi, is_file=True)
            if ret != 0:
                logging.error(f"Set speech engine hmi event error: {ret}, event: {hmi}")

        # 设置个性化信息
        grammar = self.testcase.param.get('grammar')
        if grammar:
            ret = self.tsa_session.set_event(grammar, is_file=True)
            if ret != 0:
                logging.error(f"Set speech engine grammar event error: {ret}, event: {grammar}")

        # 设置LinkType
        linkType = self.testcase.param.get('linkType')
        if linkType:
            ret = self.sre_session.set_link_type(int(linkType))
            if ret != 0:
                logging.error(f"Set speech engine link type error: {ret}, event: {linkType}")

        # 设置在线/离线策略
        strategy = self.testcase.param.get('strategy')
        if strategy:
            ret = self.tsa_session.set_event({"source": "TSA", "type": "SetStrategy", "data": {"value": int(strategy)}})
            if ret != 0:
                logging.error(f"Set strategy event error: {ret}, strategy: {strategy}")

        # 设置全时开关
        fullTimeOption = self.testcase.param.get('fullTimeOption')
        if fullTimeOption:
            ret = self.tsa_session.set_event({"source": "TSA", "type": "SetFullTimeOption", "data": {"value": int(fullTimeOption)}})
            if ret != 0:
                logging.error(f"Set fullTime option event error: {ret}, fullTimeOption: {fullTimeOption}")

        # 设置回话保持开关
        keepDialogue = self.testcase.param.get('keepDialogue')
        if keepDialogue:
            ret = self.tsa_session.set_event({"source": "TSA", "type": "SetKeepDialogue", "data": {"value": int(keepDialogue)}})
            if ret != 0:
                logging.error(f"Set keep dialogue event error: {ret}, keepDialogue: {keepDialogue}")

        # 设置协同/并行模式
        parallelSR = self.testcase.param.get('parallelSR')
        if parallelSR:
            ret = self.tsa_session.set_event({"source": "TSA", "type": "SetParallelSR", "data": {"value": int(parallelSR)}})
            if ret != 0:
                logging.error(f"Set parallelSR event error: {ret}, parallelSR: {parallelSR}")

        # 设置FreeTalk超时时间
        freetalkTimeout = self.testcase.param.get('freetalkTimeout')
        if freetalkTimeout:
            ret = self.tsa_session.set_event({"source": "TSA", "type": "SetFreetalkTimeout", "data": {"value": int(freetalkTimeout)}})
            if ret != 0:
                logging.error(f"Set freeTalkTimeout event error: {ret}, freetalkTimeout: {freetalkTimeout}")

        # 设置TTS
        tts = self.testcase.param.get('tts')
        if tts:
            tts_level = int(tts)
            if tts_level > 0:
                tts_event = {"source": "TSA", "type": "TTSStatus", "data": {"status": "ON_PLAY", "level": tts_level}}
            else:
                tts_event = {"source": "TSA", "type": "TTSStatus", "data": {"status": "ON_STOP", "level": tts_level}}
            ret = self.tsa_session.set_event(tts_event)
            if ret != 0:
                logging.error(f"Set tts level event error: {ret}, tts: {tts}")

        # 每次将会设置FreeTalk开启
        freeWakeup = self.testcase.param.get('freeWakeup')
        if freeWakeup:
            ret = self.tsa_session.set_freetalk_status(int(freeWakeup))
            if ret != 0:
                logging.error(f"Set freeWakeup event error: {ret}, freeWakeup: {freeWakeup}")

        return 0

    @classmethod
    def voice_region_parse(self):
        for item in self.testcase.expect:
            self.voiceRegionEndList.append(item.get("voiceRegion")[1])
        self.voiceRegionEndList.sort()
        self.tsa_session.set_voice_region_end_list(self.voiceRegionEndList)

    @classmethod
    def process_result(cls, status, data):
        # 断言信号
        message_type = JsonUtil.parse(data, 'type')
        if message_type == "ASSERTED":
            _data = {"status": status, "resultType": message_type, "voiceRegionEnd": data.get("voiceRegionEnd"), "recordTime": get_timestamp()}
            cls.dataBus.put(_data)
            return

        if message_type in cls.yaml_config:
            type_config = cls.yaml_config[message_type]
            result = {"status": status, "resultType": message_type, "recordTime": get_timestamp()}

            for key, value in type_config.items():
                data_value = JsonUtil.parse(data, value)
                result[key] = data_value
            channel_id = str(result.get("channelId"))

            if message_type == "NLPResult":
                source = result.get("source")
                text = result.get("text")
                skill = result.get("skill")
                intention = result.get("intention")
                dirCallbackType = result.get("dirCallbackType")
                dirCallbackEtId = result.get("dirCallbacketId")
                # VPA理解结果过滤
                if not text and skill == "VPA_ACTION" and intention == "VPA_ACTION":
                    return
                async_color_print((f"[ 理解结果 ] channel:{channel_id}\tsource:{source}\ttext:{text}\tskill:{skill}\tintention:{intention}\tcallbackType:{dirCallbackType}", "yellow"))
                async_color_print((json.dumps(data, ensure_ascii=False, separators=(',', ':')), None))
                cls.callback_event_type = dirCallbackType
                cls.callback_event_id = dirCallbackEtId
                cls.dataBus.put(result)
                cls.nluEvent.set()
            elif message_type == "ASRResult":
                asr = result.get("asr")
                source = result.get("source")
                async_color_print((f"[ 识别结果 ] channel:{channel_id}\tsource:{source}\ttext:{asr}", "yellow"))
                async_color_print((json.dumps(data, ensure_ascii=False, separators=(',', ':')), None))
                cls.dataBus.put(result)
            elif message_type == "ASRResultTemp":
                tmp_asr = result.get("asr")
                async_color_print((tmp_asr, None))
                cls.dataBus.put(result)
            elif message_type == "ASRInputResult":
                voiAsr = result.get("voiAsr")
                async_color_print((f"[ VOI Asr ] channel:{channel_id}\ttext:{voiAsr}", "blue"))
                async_color_print((json.dumps(data, ensure_ascii=False, separators=(',', ':')), None))
                cls.dataBus.put(result)
            elif message_type == "ASRInputResultTemp":
                voiAsrTemp = result.get("voiAsrTemp")
                async_color_print((f"{voiAsrTemp}", "blue"))
                cls.dataBus.put(result)

    @classmethod
    def tsa_callback_handler(cls, status, data):
        """处理TSA助手,LCSEngine平台回调事件"""
        # 断言信号
        message_type = JsonUtil.parse(data, 'type')
        if message_type == "ASSERTED":
            _data = {"status": status, "resultType": message_type, "voiceRegionEnd": data.get("voiceRegionEnd"),
                     "recordTime": get_timestamp()}
            cls.dataBus.put(_data)
            return
        callback_data = json.dumps(data, ensure_ascii=False, separators=(',', ':'))
        cls.report_pool.append(callback_data)
        if message_type in cls.yaml_config:
            type_config = cls.yaml_config[message_type]
            result = {"status": status, "resultType": message_type, "recordTime": get_timestamp()}
            for key, value in type_config.items():
                data_value = JsonUtil.parse(data, value)
                result[key] = data_value
            channel_id = str(result.get("channelId"))
            cls.callback_result.append(result)
            if message_type == "NLPResult":
                source = result.get("source")
                text = result.get("text")
                skill = result.get("skill")
                intention = result.get("intention")
                dirCallbackType = result.get("dirCallbackType")
                dirCallbackEtId = result.get("dirCallbacketId")
                # VPA理解结果过滤
                if not text and skill == "VPA_ACTION" and intention == "VPA_ACTION":
                    return
                async_color_print((f"[ 理解结果 ] channel:{channel_id}\tsource:{source}\ttext:{text}\tskill:{skill}\tintention:{intention}\tcallbackType:{dirCallbackType}", "yellow"))
                async_color_print((callback_data, None))
                cls.callback_event_type = dirCallbackType
                cls.callback_event_id = dirCallbackEtId
                cls.dataBus.put(result)
                cls.nluEvent.set()
            elif message_type == "ASRResult":
                asr = result.get("asr")
                source = result.get("source")
                async_color_print((f"[ 识别结果 ] channel:{channel_id}\tsource:{source}\ttext:{asr}", "yellow"))
                async_color_print((callback_data, None))
                cls.dataBus.put(result)
            elif message_type == "ASRResultTemp":
                tmp_asr = result.get("asr")
                async_color_print((tmp_asr, None))
                cls.dataBus.put(result)
            elif message_type == "ASRInputResult":
                voiAsr = result.get("voiAsr")
                async_color_print((f"[ VOI Asr ] channel:{channel_id}\ttext:{voiAsr}", "blue"))
                async_color_print((callback_data, None))
                cls.dataBus.put(result)
            elif message_type == "ASRInputResultTemp":
                voiAsrTemp = result.get("voiAsrTemp")
                async_color_print((f"{voiAsrTemp}", "blue"))
                cls.dataBus.put(result)
            elif message_type == "HICARWakeup":
                source = result.get("source")
                channel_id = result.get("channelId")
                types = result.get("type")
                text = result.get("text")
                cls.dataBus.put(result)
                async_color_print((f"[ HiCar唤醒结果 ][{channel_id}]\tsource:{source}\ttype:{types}\ttext:{text}", "red"))
